home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 016 / source.files / iffr.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  10KB  |  323 lines

  1. /*----------------------------------------------------------------------*
  2.  * IFFR.C  Support routines for reading IFF-85 files.          1/23/86
  3.  * (IFF is Interchange Format File.)
  4.  *
  5.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  6.  * This software is in the public domain.
  7.  *
  8.  * This version for the Commodore-Amiga computer.
  9.  *
  10.  * Uses "gio".  Either link with gio.c, or set the GIO_ACTIVE flag to 0
  11.  * in gio.h.
  12.  *----------------------------------------------------------------------*/
  13. #include "iff/gio.h"
  14. #include "iff/iff.h"
  15.  
  16. /* ----- Private subroutine FileLength() --------------------------------*/
  17. /* Returns the length of the file or else a negative IFFP error code
  18.  * (NO_FILE or DOS_ERROR). AmigaDOS-specific implementation.
  19.  * SIDE EFFECT: Thanks to AmigaDOS, we have to change the file's position
  20.  * to find its length.
  21.  * Now if Amiga DOS maintained fh_End, we'd just do this:
  22.  *    fileLength = (FileHandle *)BADDR(file)->fh_End; */
  23. LONG FileLength(file)  BPTR file;  {
  24.     LONG fileLength = NO_FILE;
  25.  
  26.     if (file > 0)  {
  27.     GSeek(file, 0, OFFSET_END);    /* Seek to end of file.*/
  28.     fileLength = GSeek(file, 0, OFFSET_CURRENT);
  29.         /* Returns position BEFORE the seek, which is #bytes in file. */
  30.     if (fileLength < 0)
  31.         fileLength = DOS_ERROR;    /* DOS being absurd.*/
  32.     }
  33.  
  34.     return(fileLength);
  35.     }
  36.  
  37. /* ---------- Read -----------------------------------------------------*/
  38.  
  39. /* ---------- OpenRIFF --------------------------------------------------*/
  40. IFFP OpenRIFF(file0, new0, clientFrame)
  41.     BPTR file0;   GroupContext *new0;  ClientFrame *clientFrame; {
  42.     register BPTR file = file0;
  43.     register GroupContext *new = new0;
  44.     IFFP iffp = IFF_OKAY;
  45.  
  46.     new->parent       = NULL;        /* "whole file" has no parent.*/
  47.     new->clientFrame  = clientFrame;
  48.     new->file         = file;
  49.     new->position     = 0;
  50.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  51.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  52.  
  53.     /* Set new->bound and go to the file's beginning. */
  54.     new->bound = FileLength(file);
  55.     if (new->bound < 0)
  56.     iffp = new->bound;           /* File system error! */
  57.     else if ( new->bound < sizeof(ChunkHeader) )
  58.     iffp = NOT_IFF;               /* Too small for an IFF file. */
  59.     else
  60.     GSeek(file, 0, OFFSET_BEGINNING);  /* Go to file start. */
  61.  
  62.     return(iffp);
  63.     }
  64.  
  65. /* ---------- OpenRGroup -----------------------------------------------*/
  66. IFFP OpenRGroup(parent0, new0)   GroupContext *parent0, *new0; {
  67.     register GroupContext *parent = parent0;
  68.     register GroupContext *new    = new0;
  69.     IFFP iffp = IFF_OKAY;
  70.  
  71.     new->parent       = parent;
  72.     new->clientFrame  = parent->clientFrame;
  73.     new->file         = parent->file;
  74.     new->position     = parent->position;
  75.     new->bound        = parent->position + ChunkMoreBytes(parent);
  76.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  77.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  78.  
  79.     if ( new->bound > parent->bound  ||  IS_ODD(new->bound) )
  80.     iffp = BAD_IFF;
  81.     return(iffp);
  82.     }
  83.  
  84. /* ---------- CloseRGroup -----------------------------------------------*/
  85. IFFP CloseRGroup(context)   GroupContext *context; {
  86.     register LONG position;
  87.  
  88.     if (context->parent == NULL) {
  89.     }  /* Context for whole file.*/
  90.     else {
  91.     position = context->position;
  92.     context->parent->bytesSoFar += position - context->parent->position;
  93.     context->parent->position = position;
  94.     }
  95.     return(IFF_OKAY);
  96.     }
  97.  
  98. /* ---------- SkipFwd --------------------------------------------------*/
  99. /* Skip over bytes in a context. Won't go backwards.*/
  100. /* Updates context->position but not context->bytesSoFar.*/
  101. /* This implementation is AmigaDOS specific.*/
  102. IFFP SkipFwd(context, bytes)   GroupContext *context;  LONG bytes; {
  103.     IFFP iffp = IFF_OKAY;
  104.  
  105.     if (bytes > 0) {
  106.     if (-1 == GSeek(context->file, bytes, OFFSET_CURRENT))
  107.         iffp = BAD_IFF;    /* Ran out of bytes before chunk complete.*/
  108.     else
  109.         context->position += bytes;
  110.     }
  111.     return(iffp);
  112.     }
  113.  
  114. /* ---------- GetChunkHdr ----------------------------------------------*/
  115. ID GetChunkHdr(context0)   GroupContext *context0;  {
  116.     register GroupContext *context = context0;
  117.     register IFFP iffp;
  118.     LONG remaining;
  119.  
  120.     /* Skip remainder of previous chunk & padding. */
  121.     iffp = SkipFwd(context,
  122.     ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
  123.     CheckIFFP();
  124.  
  125.     /* Set up to read the new header. */
  126.     context->ckHdr.ckID = BAD_IFF;    /* Until we know it's okay, mark it BAD.*/
  127.     context->subtype    = NULL_CHUNK;
  128.     context->bytesSoFar = 0;
  129.  
  130.     /* Generate a psuedo-chunk if at end-of-context. */
  131.     remaining = context->bound - context->position;
  132.     if (remaining == 0) {
  133.     context->ckHdr.ckSize = 0;
  134.     context->ckHdr.ckID   = END_MARK;
  135.     }
  136.  
  137.     /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
  138.     else if (sizeof(ChunkHeader) > remaining) {
  139.     context->ckHdr.ckSize = remaining;
  140.     }
  141.  
  142.     /* Read the chunk header (finally). */
  143.     else {
  144.         switch (
  145.         GRead(context->file, (BYTE *)&context->ckHdr, sizeof(ChunkHeader))
  146.         ) {
  147.         case -1: return(context->ckHdr.ckID = DOS_ERROR);
  148.         case 0:  return(context->ckHdr.ckID = BAD_IFF);
  149.         }
  150.  
  151.     /* Check: Top level chunk must be LIST or FORM or CAT. */
  152.     if (context->parent == NULL)
  153.         switch(context->ckHdr.ckID) {
  154.         case FORM:  case LIST:  case CAT:  break;
  155.         default:    return(context->ckHdr.ckID = NOT_IFF);
  156.         }
  157.  
  158.     /* Update the context. */
  159.     context->position += sizeof(ChunkHeader);
  160.     remaining         -= sizeof(ChunkHeader);
  161.  
  162.     /* Non-positive ID values are illegal and used for error codes.*/
  163.     /* We could check for other illegal IDs...*/
  164.     if (context->ckHdr.ckID <= 0)
  165.          context->ckHdr.ckID = BAD_IFF;
  166.  
  167.     /* Check: ckSize negative or larger than # bytes left in context? */
  168.     else if (context->ckHdr.ckSize < 0  ||
  169.          context->ckHdr.ckSize > remaining) {
  170.         context->ckHdr.ckSize = remaining;
  171.         context->ckHdr.ckID   = BAD_IFF;
  172.         }
  173.  
  174.     /* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
  175.     else switch (context->ckHdr.ckID) {
  176.         case LIST:  case FORM:  case PROP:  case CAT:  {
  177.         iffp = IFFReadBytes(context,
  178.                     (BYTE *)&context->subtype,
  179.                     sizeof(ID));
  180.         if (iffp != IFF_OKAY)
  181.             context->ckHdr.ckID = iffp;
  182.         break; }
  183.         }
  184.  
  185.     }
  186.     return(context->ckHdr.ckID);
  187.     }
  188.  
  189. /* ---------- IFFReadBytes ---------------------------------------------*/
  190. IFFP IFFReadBytes(context, buffer, nBytes)
  191.     GroupContext *context;   BYTE *buffer;   LONG nBytes; {
  192.     register IFFP iffp = IFF_OKAY;
  193.  
  194.     if (nBytes < 0)
  195.     iffp = CLIENT_ERROR;
  196.     else if (nBytes > ChunkMoreBytes(context))
  197.     iffp = SHORT_CHUNK;
  198.     else if (nBytes > 0)
  199.     switch ( GRead(context->file, buffer, nBytes) ) {
  200.         case -1: {iffp = DOS_ERROR; break; }
  201.         case 0:  {iffp = BAD_IFF;   break; }
  202.         default: {
  203.         context->position   += nBytes;
  204.         context->bytesSoFar += nBytes;
  205.         }
  206.         }
  207.  
  208.     return(iffp);
  209.     }
  210.  
  211. /* ---------- SkipGroup ------------------------------------------------*/
  212. IFFP SkipGroup(context)  GroupContext *context;  {
  213.     }    /* Nothing to do, thanks to GetChunkHdr */
  214.  
  215. /* ---------- ReadIFF --------------------------------------------------*/
  216. IFFP ReadIFF(file, clientFrame)  BPTR file;  ClientFrame *clientFrame;  {
  217.     /*CompilerBug register*/ IFFP iffp;
  218.     GroupContext context;
  219.  
  220.     iffp = OpenRIFF(file, &context);
  221.     context.clientFrame = clientFrame;
  222.  
  223.     if (iffp == IFF_OKAY)
  224.     switch (iffp = GetChunkHdr(&context)) {
  225.         case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
  226.         case LIST: { iffp = (*clientFrame->getList)(&context); break; }
  227.         case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
  228.         /* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
  229.         }
  230.  
  231.     CloseRGroup(&context);
  232.  
  233.     if (iffp > 0)        /* Make sure we don't return an ID.*/
  234.     iffp = NOT_IFF;        /* GetChunkHdr should've caught this.*/
  235.     return(iffp);
  236.     }
  237.  
  238. /* ---------- ReadIList ------------------------------------------------*/
  239. IFFP ReadIList(parent, clientFrame)
  240.     GroupContext *parent;  ClientFrame *clientFrame; {
  241.     GroupContext listContext;
  242.     IFFP iffp;
  243.     BOOL propOk = TRUE;
  244.  
  245.     iffp = OpenRGroup(parent, &listContext);
  246.     CheckIFFP();
  247.  
  248.     /* One special case test lets us handle CATs as well as LISTs.*/
  249.     if (parent->ckHdr.ckID == CAT)
  250.     propOk = FALSE;
  251.     else
  252.     listContext.clientFrame = clientFrame;
  253.  
  254.     do {
  255.     switch (iffp = GetChunkHdr(&listContext)) {
  256.         case PROP: {
  257.         if (propOk)
  258.             iffp = (*clientFrame->getProp)(&listContext);
  259.         else
  260.             iffp = BAD_IFF;
  261.         break;
  262.         }
  263.         case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
  264.         case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
  265.         case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
  266.         /* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
  267.         }
  268.     if (listContext.ckHdr.ckID != PROP)
  269.         propOk = FALSE;    /* No PROPs allowed after this point.*/
  270.     } while (iffp == IFF_OKAY);
  271.  
  272.     CloseRGroup(&listContext);
  273.  
  274.     if (iffp > 0)    /* Only chunk types above are allowed in a LIST/CAT.*/
  275.     iffp = BAD_IFF;
  276.     return(iffp == END_MARK ? IFF_OKAY : iffp);
  277.     }
  278.  
  279. /* ---------- ReadICat -------------------------------------------------*/
  280. /* By special arrangement with the ReadIList implement'n, this is trivial.*/
  281. IFFP ReadICat(parent)  GroupContext *parent;  {
  282.     return( ReadIList(parent, NULL) );
  283.     }
  284.  
  285. /* ---------- GetFChunkHdr ---------------------------------------------*/
  286. ID GetFChunkHdr(context)   GroupContext *context; {
  287.     register ID id;
  288.  
  289.     id = GetChunkHdr(context);
  290.     if (id == PROP)
  291.     context->ckHdr.ckID = id = BAD_IFF;
  292.     return(id);
  293.     }
  294.  
  295. /* ---------- GetF1ChunkHdr ---------------------------------------------*/
  296. ID GetF1ChunkHdr(context)   GroupContext *context; {
  297.     register ID id;
  298.     register ClientFrame *clientFrame = context->clientFrame;
  299.  
  300.     switch (id = GetChunkHdr(context))  {
  301.     case PROP: { id = BAD_IFF; break; }
  302.     case FORM: { id = (*clientFrame->getForm)(context); break; }
  303.     case LIST: { id = (*clientFrame->getList)(context); break; }
  304.     case CAT : { id = (*clientFrame->getCat )(context); break; }
  305.     /* Default: let the caller handle other chunks */
  306.     }
  307.     return(context->ckHdr.ckID = id);
  308.     }
  309.  
  310. /* ---------- GetPChunkHdr ---------------------------------------------*/
  311. ID GetPChunkHdr(context)   GroupContext *context; {
  312.     register ID id;
  313.  
  314.     id = GetChunkHdr(context);
  315.     switch (id) {
  316.     case LIST:  case FORM:  case PROP:  case CAT:  {
  317.         id = context->ckHdr.ckID = BAD_IFF;
  318.         break; }
  319.     }
  320.     return(id);
  321.     }
  322.  
  323.